//-
// Copyright 2010 Autodesk, Inc.  All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk license agreement
// provided at the time of installation or download, or which otherwise
// accompanies this software in either electronic or hard copy form.
//+

#include <stdio.h>

#include <maya/MIOStream.h>
#include <maya/MGlobal.h>
#include <maya/MString.h>
#include <maya/MFnPlugin.h>

#include <maya/MPxCommand.h>
#include <maya/MSyntax.h>
#include <maya/MArgDatabase.h>
#include <maya/MArgList.h>
#include <maya/MSelectionList.h>
#include <maya/MItSelectionList.h>

// For sample draw traversal
#include <maya/MDrawTraversal.h>
#include <maya/MHardwareRenderer.h>
#include <maya/MFnDagNode.h>
#include <maya/MBoundingBox.h>

#include <maya/M3dView.h>
#include <maya/MPoint.h>
#include <maya/MImage.h>
#include <maya/MViewport2Renderer.h>
#include <maya/MRenderTargetManager.h>
#include <maya/MShaderManager.h>
#include <maya/MTextureManager.h>
#include <maya/MDrawContext.h>

//////////////////////////////////////////////////////////////////////////
//
// Trivial scene draw classes
//
//------------------------------------------------------------------------
// Draw traversal utility
//
class MSurfaceDrawTraversal : public MDrawTraversal
{
	virtual bool		filterNode( const MDagPath &traversalItem )
	{
		bool prune = false;

		// Check to only prune shapes, not transforms.
		//
		if ( traversalItem.childCount() == 0)
		{
			if ( !traversalItem.hasFn( MFn::kMesh) &&
				!traversalItem.hasFn( MFn::kNurbsSurface) &&
				!traversalItem.hasFn( MFn::kSubdiv)
				)
			{
				prune = true;
			}
		}
		return prune;
	}
};

static MGLFunctionTable *gGLFT = NULL;

//------------------------------------------------------------------------
/*
	A very simplistic custom scene draw example which just draws
	coloured bounding boxes for surface types.

	This code is similiar to the example code found in the
	plugin example OpenGLViewportRenderer and just
	traverses the scene to perform a draw.
*/
class MCustomSceneDraw
{
public:
	MCustomSceneDraw()
	{
		if ( !gGLFT )
			gGLFT = MHardwareRenderer::theRenderer()->glFunctionTable();
	}
	~MCustomSceneDraw()
	{
		gGLFT = NULL;
	}

	// Draw a bounding box
	//
	void drawBounds( const MDagPath &dagPath,
					 const MBoundingBox &box)
	{
		MMatrix  matrix = dagPath.inclusiveMatrix();
		MPoint	minPt = box.min();
		MPoint	maxPt = box.max();

		double bottomLeftFront[3] =		{ minPt.x, minPt.y, minPt.z };
		double topLeftFront[3] = 		{ minPt.x, maxPt.y, minPt.z };
		double bottomRightFront[3] =	{ maxPt.x, minPt.y, minPt.z };
		double topRightFront[3] =		{ maxPt.x, maxPt.y, minPt.z };
		double bottomLeftBack[3] =		{ minPt.x, minPt.y, maxPt.z };
		double topLeftBack[3] =			{ minPt.x, maxPt.y, maxPt.z };
		double bottomRightBack[3] =		{ maxPt.x, minPt.y, maxPt.z };
		double topRightBack[3] =		{ maxPt.x, maxPt.y, maxPt.z };

		gGLFT->glMatrixMode( MGL_MODELVIEW );
		gGLFT->glPushMatrix();
		gGLFT->glMultMatrixd( &(matrix.matrix[0][0]) );

		gGLFT->glBegin(MGL_LINE_STRIP);
		gGLFT->glVertex3dv( bottomLeftFront );
		gGLFT->glVertex3dv( bottomLeftBack );
		gGLFT->glVertex3dv( topLeftBack );
		gGLFT->glVertex3dv( topLeftFront );
		gGLFT->glVertex3dv( bottomLeftFront );
		gGLFT->glVertex3dv( bottomRightFront );
		gGLFT->glVertex3dv( bottomRightBack);
		gGLFT->glVertex3dv( topRightBack );
		gGLFT->glVertex3dv( topRightFront );
		gGLFT->glVertex3dv( bottomRightFront );
		gGLFT->glEnd();

		gGLFT->glBegin(MGL_LINES);
		gGLFT->glVertex3dv(bottomLeftBack);
		gGLFT->glVertex3dv(bottomRightBack);

		gGLFT->glVertex3dv(topLeftBack);
		gGLFT->glVertex3dv(topRightBack);

		gGLFT->glVertex3dv(topLeftFront);
		gGLFT->glVertex3dv(topRightFront);
		gGLFT->glEnd();

		gGLFT->glPopMatrix();
	}

	// Draw a scene full of bounding boxes
	//
	bool draw(MDagPath &cameraPath, unsigned int width, unsigned int height)
	{
		if (!cameraPath.isValid())
			return false;

		MDrawTraversal *trav = NULL;
		trav = new MSurfaceDrawTraversal;

		if (!trav)
			return false;

		trav->enableFiltering( true );
		trav->setFrustum( cameraPath, width, height );
		if (!trav->frustumValid())
		{
			delete trav; trav = NULL;
			return false;
		}
		trav->traverse();

		unsigned int numItems = trav->numberOfItems();
		unsigned int i;
		for (i=0; i<numItems; i++)
		{
			MDagPath path;
			trav->itemPath(i, path);

			if (path.isValid())
			{
				bool drawIt = false;

				//
				// Draw surfaces (polys, nurbs, subdivs)
				//
				bool active = false;
				bool templated = false;
				if ( path.hasFn( MFn::kMesh) ||
					path.hasFn( MFn::kNurbsSurface) ||
					path.hasFn( MFn::kSubdiv) )
				{
					drawIt = true;
					if (trav->itemHasStatus( i, MDrawTraversal::kActiveItem ))
					{
						active = true;
						gGLFT->glColor3f( 1.0f, 1.0f, 1.0f );
					}
					else if (trav->itemHasStatus( i, MDrawTraversal::kTemplateItem ))
					{
						gGLFT->glColor3f( 0.2f, 0.2f, 0.2f );
						templated = true;
					}
					else
					{
						if (path.hasFn( MFn::kMesh ))
							gGLFT->glColor3f( 0.286f, 0.706f, 1.0f );
						else if (path.hasFn( MFn::kNurbsSurface))
							gGLFT->glColor3f( 0.486f, 0.306f, 1.0f );
						else
							gGLFT->glColor3f( 0.886f, 0.206f, 1.0f );
					}
				}

				if (drawIt)
				{
					MFnDagNode dagNode(path);
					MBoundingBox box = dagNode.boundingBox();
					drawBounds( path, box );
				}
			}
		}

		if (trav)
		{
			delete trav;
			trav = NULL;
		}
		return true;
	}
};

//////////////////////////////////////////////////////////////////////////
//
// Render override classes
//
class viewRenderOverride;


//------------------------------------------------------------------------
// Custom present target operation
//
class viewRenderPresentTarget : public MHWRender::MPresentTarget
{
public:
	viewRenderPresentTarget(const MString &name)
	: MPresentTarget( name )
	{
		mTargets[0] = mTargets[1] = NULL;
	}

	~viewRenderPresentTarget()
	{
		mTargets[0] = mTargets[1] = NULL;
	}

	virtual MHWRender::MRenderTarget** targetOverrideList(unsigned int &listSize)
	{
		if (mTargets[0] && mTargets[1])
		{
			listSize = 2;
			return &mTargets[0];
		}
		return NULL;
	}

	void setRenderTarget(unsigned int i, MHWRender::MRenderTarget* val)
	{
		if (i<2)
			mTargets[i] = val;
	}
protected:
	// Targets used as input parameters to mShaderInstance;
	MHWRender::MRenderTarget* mTargets[2];
};

//------------------------------------------------------------------------
// Custom quad operation
//
class viewRenderQuadRender : public MHWRender::MQuadRender
{
public:
	// Shader list
	typedef enum {
		kEffectNone,
		kPost_EffectMonochrome,
		kPost_EffectEdgeDetect,
		kPost_EffectInvert,
		kScene_Threshold,
		kScene_BlurHoriz,
		kScene_BlurVert,
		kSceneBlur_Blend,
		kPre_MandelBrot,
	} MQuadShader ;

	viewRenderQuadRender(const MString &name)
		: MQuadRender( name )
		, mShaderInstance(NULL)
		, mShader(kEffectNone)
	{
		mTargets[0] = mTargets[1] = NULL;
	}

	~viewRenderQuadRender()
	{
		mTargets[0] = mTargets[1] = NULL;
		if (mShaderInstance)
		{
			delete mShaderInstance;
			mShaderInstance = NULL;
		}
	}

	// Shader override
	virtual const MHWRender::MShaderInstance * shader()
	{
		bool postQuadRender = true;
		if (postQuadRender)
		{
			// Create a new shader instance to be used for quad rendering.
			if (mShaderInstance == NULL)
			{
				MHWRender::MRenderer* renderer = MHWRender::MRenderer::theRenderer();
				if (renderer)
				{
					const MHWRender::MShaderManager* shaderMgr = renderer->getShaderManager();
					if (shaderMgr)
					{
						switch (mShader)
						{
						case kPre_MandelBrot:
							mShaderInstance = shaderMgr->getEffectsFileShader( "MandelBrotGL.cgfx", "" );
							break;
						case kPost_EffectMonochrome:
							mShaderInstance = shaderMgr->getEffectsFileShader( "FilterMonochrome.cgfx", "" );
							break;
						case kPost_EffectEdgeDetect:
							mShaderInstance = shaderMgr->getEffectsFileShader( "FilterEdgeDetect.cgfx", "" );
							break;
						case kPost_EffectInvert:
							mShaderInstance = shaderMgr->getEffectsFileShader( "InvertGL.cgfx", "" );
							break;
						case kScene_Threshold:
							mShaderInstance = shaderMgr->getEffectsFileShader( "ThresholdGL.cgfx", "" );
							break;
						case kScene_BlurHoriz:
							mShaderInstance = shaderMgr->getEffectsFileShader( "BlurGL.cgfx", "BlurHoriz" );
							break;
						case kScene_BlurVert:
							mShaderInstance = shaderMgr->getEffectsFileShader( "BlurGL.cgfx", "BlurVert" );
							break;
						case kSceneBlur_Blend:
							mShaderInstance = shaderMgr->getEffectsFileShader( "BlendGL.cgfx", "Add" );
							break;
						default:
							break;
						}
					}
				}
			}
			if (mShaderInstance)
			{
				switch (mShader)
				{
				case kPre_MandelBrot:
					{
						MStatus status = mShaderInstance->setParameter("gIterate", 50);
						if (status != MStatus::kSuccess)
						{
							printf("Could not change mandelbrot parameter\n");
							return NULL;
						}
					}
					break;

				case kPost_EffectInvert:
					{
						MHWRender::MRenderTargetAssignment assignment;
						assignment.target = mTargets[0];
						MStatus status = mShaderInstance->setParameter("gInputTex", assignment);
						if (status != MStatus::kSuccess)
						{
							printf("Could not set input render target / texture parameter on invert shader\n");
							return NULL;
						}
					}
					break;

				case kScene_Threshold:
					{
						MHWRender::MRenderTargetAssignment assignment;
						assignment.target = mTargets[0];
						MStatus status = mShaderInstance->setParameter("gSourceTex", assignment);
						if (status != MStatus::kSuccess)
						{
							printf("Could not set input render target / texture parameter on threshold shader\n");
							return NULL;
						}
						mShaderInstance->setParameter("gBrightThreshold", 0.7f );
					}
					break;

				case kScene_BlurHoriz:
					{
						MHWRender::MRenderTargetAssignment assignment;
						assignment.target = mTargets[2];
						MStatus status = mShaderInstance->setParameter("gSourceTex", assignment);
						if (status != MStatus::kSuccess)
						{
							printf("Could not set input render target / texture parameter on hblur shader\n");
							return NULL;
						}
					}
					break;

				case kScene_BlurVert:
					{
						MHWRender::MRenderTargetAssignment assignment;
						assignment.target = mTargets[2];
						MStatus status = mShaderInstance->setParameter("gSourceTex", assignment);
						if (status != MStatus::kSuccess)
						{
							printf("Could not set input render target / texture parameter on vlur shader\n");
							return NULL;
						}
					}
					break;

				case kSceneBlur_Blend:
					{
						// Source is scene + blur
						MHWRender::MRenderTargetAssignment assignment;
						assignment.target = mTargets[0];
						MStatus status = mShaderInstance->setParameter("gSourceTex", assignment);
						if (status != MStatus::kSuccess)
						{
							return NULL;
						}
						MHWRender::MRenderTargetAssignment assignment2;
						assignment2.target = mTargets[2];
						status = mShaderInstance->setParameter("gSourceTex2", assignment2);
						if (status != MStatus::kSuccess)
						{
							return NULL;
						}
						mShaderInstance->setParameter("gBlendSrc", 0.3f );
					}
					break;

				case kPost_EffectMonochrome:
					{
						MHWRender::MRenderTargetAssignment assignment;
						assignment.target = mTargets[0];
						MStatus status = mShaderInstance->setParameter("gInputTex", assignment);
						if (status != MStatus::kSuccess)
						{
							printf("Could not set input render target / texture parameter on monochrome shader\n");
							return NULL;
						}
					}
					break;

				case kPost_EffectEdgeDetect:
					{
						MHWRender::MRenderTargetAssignment assignment;
						assignment.target = mTargets[0];
						MStatus status = mShaderInstance->setParameter("gInputTex", assignment);
						if (status != MStatus::kSuccess)
						{
							printf("Could not set input render target / texture parameter on edge detect shader\n");
							return NULL;
						}
						mShaderInstance->setParameter("gThickness", 1.0f );
						mShaderInstance->setParameter("gThreshold", 0.1f );
					}
					break;

				default:
					break;
				}
			}
		}
		return mShaderInstance;
	}

	virtual MHWRender::MRenderTarget** targetOverrideList(unsigned int &listSize)
	{
		// Render to blur target for blur operations
		if (mShader == kScene_Threshold ||
			mShader == kScene_BlurHoriz ||
			mShader == kScene_BlurVert )
		{
			listSize = 1;
			return &mTargets[2];
		}
		// Render to final otherwise
		else
		{
			if (mTargets[0] && mTargets[1])
			{
				listSize = 2;
				return &mTargets[0];
			}
		}
		return NULL;
	}

	virtual MHWRender::MClearOperation & clearOperation()
	{
		// Want to clear everything since the quad render is the first operation.
		if (mShader == kPre_MandelBrot)
		{
			mClearOperation.setClearGradient( false );
			mClearOperation.setMask( (unsigned int) MHWRender::MClearOperation::kClearAll );
		}
		// Is a post operation, don't want to clear anything
		else
		{
			mClearOperation.setClearGradient( false );
			mClearOperation.setMask( (unsigned int) MHWRender::MClearOperation::kClearNone );
		}
		return mClearOperation;
	}

	void setRenderTarget(unsigned int i, MHWRender::MRenderTarget* val)
	{
		if (i<3)
			mTargets[i] = val;
	}

	void setShader( MQuadShader shader )
	{
		mShader = shader;
	}
	MQuadShader shader() const
	{
		return mShader;
	}

	const MFloatPoint & viewRectangle() const
	{
		return mViewRectangle;
	}
	void setViewRectangle(const MFloatPoint & rect)
	{
		mViewRectangle = rect;
	}

protected:
	// Shader to use for the quad render
	MHWRender::MShaderInstance *mShaderInstance;
	// Targets used as input parameters to mShaderInstance;
	MHWRender::MRenderTarget* mTargets[3];
	// View rectangle
	MFloatPoint mViewRectangle;

	// Shader to use for quad rendering
	MQuadShader mShader;
};

//------------------------------------------------------------------------

// Simple scene operation
//
class simpleViewRenderSceneRender : public MHWRender::MSceneRender
{
public:
    simpleViewRenderSceneRender(const MString &name)
        : MSceneRender( name )
   {
   }
};

// Custom scene operation
//
// Collection of a few simple things that can
// be done with a scene render.
//
class viewRenderSceneRender : public MHWRender::MSceneRender
{
public:
	viewRenderSceneRender(const MString &name)
		: MSceneRender( name )
	{
		mColorCount = 0;
		mPrevDisplayStyle = M3dView::kGouraudShaded;

		// 100 % of target size
		mViewRectangle[0] = 0.0f;
		mViewRectangle[1] = 0.0f;
		mViewRectangle[2] = 1.0f;
		mViewRectangle[3] = 1.0f;

		mTargets[0] = mTargets[1] = NULL;

		mShaderOverride = NULL;

		mSplitUI = false;
	}
	virtual ~viewRenderSceneRender()
	{
		delete mShaderOverride;
		mShaderOverride = NULL;
	}

	// Update current render targets
	void setRenderTarget(unsigned int i, MHWRender::MRenderTarget* val)
	{
		if (i<2)
			mTargets[i] = val;
	}

	// Offscreen target override
	//
	virtual MHWRender::MRenderTarget** targetOverrideList(unsigned int &listSize)
	{
		listSize = 0;
		if (mTargets[0] && mTargets[1])
		{
			listSize = 2;
		}
		return &(mTargets[0]);
	}

	// Camera override
	virtual const MHWRender::MCameraOverride * cameraOverride()
	{
		M3dView mView;
		if (mPanelName.length() &&
			(M3dView::getM3dViewFromModelPanel(mPanelName, mView) == MStatus::kSuccess))
		{
			mView.getCamera( mCameraOverride.mCameraPath );
			return &mCameraOverride;
		}

		printf("\t%s : Query custom scene camera override -- no override set\n", mName.asChar());
		return NULL;
	}

	// Render filter override
	virtual MHWRender::MSceneRender::MSceneFilterOption renderFilterOverride()
	{
		bool drawShaded = true;
		bool drawUI = true;

		if (mName == "__MyStdSceneRenderUI")
		{
			drawShaded = false;
			drawUI = true;
		}
		else if (mName == "__MyStdSceneRenderNonUI")
		{
			drawShaded = true;
			drawUI = false;
		}
		unsigned int
			filterOption = MHWRender::MSceneRender::kNoSceneFilterOverride;
		if (drawShaded)
			filterOption = filterOption | MHWRender::MSceneRender::kRenderShadedItems;
		if (drawUI)
			filterOption = filterOption | MHWRender::MSceneRender::kRenderNonShadedItems;

		return (MHWRender::MSceneRender::MSceneFilterOption) filterOption;
	}

	// Display mode override
	virtual MHWRender::MSceneRender::MDisplayMode displayModeOverride()
	{
		bool userOverride = true;
		if (userOverride)
		{
			return MHWRender::MSceneRender::kShaded;
		}
		return MHWRender::MSceneRender::kNoDisplayModeOverride;
	}

	// Lighting mode override
	virtual MHWRender::MSceneRender::MLightingMode lightModeOverride()
	{
		bool userOverride = false;
		if (userOverride)
		{
			return MHWRender::MSceneRender::kSceneLights;
		}
		return MHWRender::MSceneRender::kNoLightingModeOverride;
	}

	// Culling mode override
	virtual MHWRender::MSceneRender::MCullingOption cullingOverride()
	{
		bool userOverride = false;
		if (userOverride)
		{
			return MHWRender::MSceneRender::kCullBackFaces;
		}
		return MHWRender::MSceneRender::kNoCullingOverride;
	}

	// Pre-render
	virtual void preRender()
	{
		// Example of setting the display style for the given panel / view
		// via M3dView vs as via the scene operation
		//
		bool setDisplayStyleViaView = false;
		if ( setDisplayStyleViaView )
		{
			M3dView mView;
			if (mPanelName.length() &&
				(M3dView::getM3dViewFromModelPanel(mPanelName, mView) == MStatus::kSuccess))
			{
				// Simple example of forcing to filled draw
				mPrevDisplayStyle = mView.displayStyle();
				mView.setDisplayStyle( M3dView::kGouraudShaded );
			}
		}
	}

	// Post-render
	virtual void postRender()
	{
		bool getResourceHandle = false;
		if (getResourceHandle)
		{
			// Get the id's for the textures which are used as the color and
			// depth render targets. These id's could arbitrarily change
			// so they should not be held on to.
			void * colorResourceHandle = (mTargets[0]) ? mTargets[0]->resourceHandle() : NULL;
			if (colorResourceHandle)
				printf("\t - Color target resource handle = %d\n", *( (int *)colorResourceHandle) );
			void * depthStencilResourceHandle = (mTargets[1]) ? mTargets[1]->resourceHandle() : NULL;
			if (depthStencilResourceHandle)
				printf("\t - Depth target resource handle = %d\n", *( (int *)depthStencilResourceHandle) );
		}

		// Example of set the display style for the given panel / view
		// via M3dView vs using the scene operation override
		//
		bool setDisplayStyleViaView = false;
		if ( setDisplayStyleViaView )
		{
			M3dView mView;
			if (mPanelName.length() &&
				(M3dView::getM3dViewFromModelPanel(mPanelName, mView) == MStatus::kSuccess))
			{
				// Simple example of restoring display style
				mView.setDisplayStyle( mPrevDisplayStyle );
			}
		}
	}

	// Object type exclusions
	virtual
		MHWRender::MSceneRender::MObjectTypeExclusions objectTypeExclusions()
	{
		// Example of hiding by type.
		return (MHWRender::MSceneRender::MObjectTypeExclusions )
			( MHWRender::MSceneRender::kExcludeCameras |
			  MHWRender::MSceneRender::kExcludeGrid );
	}

	// Scene override logic
	virtual const MSelectionList * objectSetOverride()
	{
		mSelectionList.clear();

		// If you set this to true you can make the
		// scene draw draw no part of the scene, only the
		// additional UI elements
		//
		bool turnOffSceneDraw = false;
		if (turnOffSceneDraw)
			return &mSelectionList;

		// Turn this on to query the active list and only
		// use that for drawing
		//
		bool filterSelection = false;
		if (filterSelection)
		{
			MSelectionList selList;
			MGlobal::getActiveSelectionList( selList );
			if (selList.length())
			{
				MItSelectionList iter( selList );
				for ( ; !iter.isDone(); iter.next() )
				{
					MDagPath item;
					MObject component;
					iter.getDagPath( item, component );
					/* MStatus sStatus = */ mSelectionList.add( item, component );
					/*
					if (sStatus == MStatus::kSuccess)
					{
					printf("Add selection item to other list\n");
					}
					else if (sStatus == MStatus::kInvalidParameter)
					{
					printf("Can't Add invalid selection item to other list\n");
					}
					else
					printf("Can't Add selection item to other list\n");
					*/
				}
			}

			if (mSelectionList.length())
			{
				printf("\t%s : Filtering render with active object list\n",
					mName.asChar());
				return &mSelectionList;
			}
		}
		return NULL;
	}

	// Custom clear override
	virtual MHWRender::MClearOperation & clearOperation()
	{
		if (mSplitUI)
		{
			if (mName == "__MyStdSceneRenderNonUI")
			{
				mClearOperation.setClearGradient( false );
				mClearOperation.setMask( (unsigned int)
					( MHWRender::MClearOperation::kClearDepth |
					MHWRender::MClearOperation::kClearStencil ) );
			}
			else if (mName == "__MyStdSceneRenderUI")
			{
				mClearOperation.setClearGradient( false );
				mClearOperation.setMask( (unsigned int) ( MHWRender::MClearOperation::kClearStencil ));
			}
		}
		else
		{

			// Setup custom clear color. Just some trial code to
			// change the background color every frame.
			//

			// Force to these colors
			float val1[4] = { 0.0f, 0.2f, 0.8f, 1.0f };
			float val2[4] = { 0.5f, 0.4f, 0.1f, 1.0f };

			//val1[0] += (float)mColorCount / 30.0f;
			//val2[1] += (float)mColorCount / 30.0f;
			mClearOperation.setClearColor( val1 );
			mClearOperation.setClearColor2( val2 );

			// Only clear every N frames to show clear control.
			//
			mColorCount  = 0;
			bool doClear = (mColorCount == 0);
			if (doClear)
			{
				// Force a gradient clear
				mClearOperation.setClearGradient( true );
				//mClearOperation.setMask( (unsigned int) MHWRender::MClearOperation::kClearAll );
				mClearOperation.setMask( (unsigned int)
					( MHWRender::MClearOperation::kClearDepth |
					MHWRender::MClearOperation::kClearStencil ) );
			}
			else
			{
				// Turn off clear and clear gradient as well as depth clear
				mClearOperation.setClearGradient( false );
				mClearOperation.setMask( (unsigned int) ( MHWRender::MClearOperation::kClearStencil ));
			}

			mColorCount++;
			if (mColorCount > 30)
				mColorCount = 0;
		}
		return mClearOperation;
	}

	// Shader override
	virtual const MHWRender::MShaderInstance* shaderOverride()
	{
		bool useShaderOverride = false;
		if (useShaderOverride)
		{
			if (!mShaderOverride)
			{
				MHWRender::MRenderer *theRenderer = MHWRender::MRenderer::theRenderer();
				const MHWRender::MShaderManager *shaderManager = theRenderer ? theRenderer->getShaderManager() : NULL;
				if (shaderManager)
				{
					bool useStockShader = true;
					if (!useStockShader)
					{
						mShaderOverride = shaderManager-> getEffectsFileShader( "GoochGL.cgfx", "" );
					}
					else
					{
						mShaderOverride = shaderManager->getStockShader( MHWRender::MShaderManager::k3dBlinnShader );
						if (mShaderOverride)
						{
							printf("\t%s : Set stock shader override %d\n", mName.asChar(), MHWRender::MShaderManager::k3dBlinnShader );
							static const float diffColor[] = {0.0f, 0.4f, 1.0f, 1.0f};
							MStatus status = mShaderOverride->setParameter("diffuseColor", diffColor);
							if (status != MStatus::kSuccess)
							{
								printf("Could not set diffuseColor on shader\n");
							}
						}
					}
				}
			}
			return mShaderOverride;
		}
		return NULL;
	}

	const MString & panelName() const
	{
		return mPanelName;
	}
	void setPanelName( const MString & name )
	{
		mPanelName.set( name.asChar() );
	}
	const MFloatPoint & viewRectangle() const
	{
		return mViewRectangle;
	}
	void setViewRectangle(const MFloatPoint & rect)
	{
		mViewRectangle = rect;
	}

	MHWRender::MRenderTarget* colorTarget() const { return mTargets[0]; }
	MHWRender::MRenderTarget* depthTarget() const { return mTargets[1]; }

	bool splitUI() const
	{
		return mSplitUI;
	}
	void setSplitUI(bool val)
	{
		mSplitUI = val;
	}

protected:
	unsigned int mColorCount;
	MSelectionList mSelectionList;
	MString mPanelName;
	M3dView::DisplayStyle mPrevDisplayStyle;

	MHWRender::MCameraOverride mCameraOverride;
	MFloatPoint mViewRectangle;

	MHWRender::MRenderTarget* mTargets[2];

	MHWRender::MShaderInstance *mShaderOverride;

	bool	mSplitUI;
};

//------------------------------------------------------------------------
/*
	Custom user operation. One approach to adding a pre and
	post scene callback. In this approach only 1 operation
	is reused twice with internal state as to when it is being
	used. Another approach which may be more suitable for when
	global state is changed is to create 2 instances of this
	operation and keep global state on the override instead of
	locally here.

	The cost of an override is very small so creating more instances
	can provide a clearer and cleaner render loop logic.
*/
class viewRenderUserOperation : public MHWRender::MUserRenderOperation
{
public:
	viewRenderUserOperation(const MString &name)
		: MUserRenderOperation( name )
	{
		// 100 % of target size
		mViewRectangle[0] = 0.0f;
		mViewRectangle[1] = 0.0f;
		mViewRectangle[2] = 1.0f;
		mViewRectangle[3] = 1.0f;
	}
	virtual ~viewRenderUserOperation()
	{
	}

	virtual MStatus execute( const MHWRender::MDrawContext & drawContext )
	{
		// Example code to find the active override.
		// This is not necessary if the operations just keep a reference
		// to the override, but this demonstrates how this
		// contextual information can be extracted.
		//
		MHWRender::MRenderer *theRenderer = MHWRender::MRenderer::theRenderer();
		const MHWRender::MRenderOverride *overridePtr = NULL;
		if (theRenderer)
		{
			const MString & overrideName = theRenderer->activeRenderOverride();
			overridePtr = theRenderer->findRenderOverride( overrideName );
		}

		// Some sample code to print out lighting information
		//
		bool dumpLightInfo = false;
		if (dumpLightInfo)
		{
			unsigned int lightCount = drawContext.numberOfActiveLights();
			if (lightCount)
			{
				MFloatPoint position;
				MFloatVector direction;
				float intensity;
				MColor color;
				bool hasDirection;
				bool hasPosition;

				printf("\tNumber of active lights = %d\n", lightCount);
				for (unsigned int i=0; i<lightCount; i++)
				{
					drawContext.getLightInformation( i,
						position, direction,
						intensity, color, hasDirection,
						hasPosition );
					printf("\tLight %d {\n", i);
					printf("\t\tDirectional %d, Positional %d\n", hasDirection, hasPosition);
					printf("\t\tDirection = %g, %g, %g. Position = %g, %g, %g\n",
						direction[0],direction[1],direction[2],
						position[0], position[1], position[2]);
					printf("\t\tColor = %g, %g, %g\n", color[0], color[1], color[2]);
					printf("\t\tIntensity = %g\n", intensity);
					printf("\t}\n");
				}
			}
		}

		// Some test code for MDrawContext
		//
		bool debugDrawContext = false;
		if (debugDrawContext)
		{
			MStatus status;
			MMatrix matrix = drawContext.getMatrix(MHWRender::MDrawContext::kWorldMtx, &status);
			double dest[4][4];
			status = matrix.get(dest);
			printf("World matrix is:\n");
			printf("\t%f, %f, %f, %f\n", dest[0][0], dest[0][1], dest[0][2], dest[0][3]);
			printf("\t%f, %f, %f, %f\n", dest[1][0], dest[1][1], dest[1][2], dest[1][3]);
			printf("\t%f, %f, %f, %f\n", dest[2][0], dest[2][1], dest[2][2], dest[2][3]);
			printf("\t%f, %f, %f, %f\n", dest[3][0], dest[3][1], dest[3][2], dest[3][3]);

			MDoubleArray viewDirection = drawContext.getTuple(MHWRender::MDrawContext::kViewDirection, &status);
			printf("Viewdirection is: %f, %f, %f\n", viewDirection[0], viewDirection[1], viewDirection[2]);

			MBoundingBox box = drawContext.getSceneBox(&status);
			printf("Screen box is:\n");
			printf("\twidth=%f, height=%f, depth=%f\n", box.width(), box.height(), box.depth());
			float center[4];
			box.center().get(center);
			printf("\tcenter=(%f, %f, %f, %f)\n", center[0], center[1], center[2], center[3]);


			int originX, originY, width, height;
			status = drawContext.getViewportDimensions(originX, originY, width, height);
			printf("Viewport dimension: center(%d, %d), width=%d, heigh=%d\n", originX, originY, width, height);


		}

		//  Draw some addition things after the scene draw
		//
		M3dView mView;
		if (mPanelName.length() &&
			(M3dView::getM3dViewFromModelPanel(mPanelName, mView) == MStatus::kSuccess))
		{
			// Get the current viewport and scale it relative to that
			//
			int targetW, targetH;
			drawContext.getRenderTargetSize( targetW, targetH );

			bool drawString = false;
			if (drawString)
			{
				MString testString("Drawing with override: ");
				testString += overridePtr->name();
				MPoint pos(0.0,0.0,0.0);
				glColor3f( 1.0f, 1.0f, 1.0f );
				mView.drawText( testString, pos);
			}

			// Some user drawing of scene bounding boxes
			//
			bool drawBoundingBoxes = false;
			if (drawBoundingBoxes)
			{
				MDagPath cameraPath;
				mView.getCamera( cameraPath);
				MCustomSceneDraw userDraw;
				userDraw.draw( cameraPath, targetW, targetH );
			}

			// A very trivial example to show how it is possible to
			// read the pixel buffer for viewports. This is the same
			// code as is used in the "viewCallbackTest"
			// plugin example, but instead of being message
			// based the user operation is intercepting
			// the pixel contents before final display
			//
			// Note that this is not recommended when using
			// offscreen targets as it may change the current context.
			// It is better to get the resource handle and perform
			// a texture blit using a shader instead.
			bool invertImageInSoftware = false;
			if (invertImageInSoftware)
			{
				MImage image;
				mView.readColorBuffer( image, true );

				unsigned char *pixelPtr = (unsigned char*)image.pixels();
				if (pixelPtr)
				{
					unsigned int width, height;
					image.getSize( width, height );

					MImage image2;
					image2.create( width, height );
					unsigned char *pixelPtr2 = (unsigned char*)image2.pixels();

					unsigned int numPixels = width * height;
					for (unsigned int i=0; i < numPixels; i++)
					{
						*pixelPtr2 = (255 - *pixelPtr);
						pixelPtr2++; pixelPtr++;
						*pixelPtr2 = (255 - *pixelPtr);
						pixelPtr2++; pixelPtr++;
						*pixelPtr2 = (255 - *pixelPtr);
						pixelPtr2++; pixelPtr++;
						*pixelPtr2 = 255;
						pixelPtr2++; pixelPtr++;
					}

					// Write all pixels back. The origin of the image (lower left)
					// is used
					mView.writeColorBuffer( image2 );
				}
			}
		}
		return MStatus::kSuccess;
	}

	// Camera override
	virtual const MHWRender::MCameraOverride * cameraOverride()
	{
		M3dView mView;
		if (mPanelName.length() &&
			(M3dView::getM3dViewFromModelPanel(mPanelName, mView) == MStatus::kSuccess))
		{
			mView.getCamera( mCameraOverride.mCameraPath );
			return &mCameraOverride;
		}
		return NULL;
	}

	// Target override
	virtual MHWRender::MRenderTarget** targetOverrideList(unsigned int &listSize)
	{
		if (mTargets[0] && mTargets[1])
		{
			listSize = 2;
			return &mTargets[0];
		}
		return NULL;
	}

	void setRenderTarget(unsigned int i, MHWRender::MRenderTarget* val)
	{
		if (i<2)
			mTargets[i] = val;
	}

	const MString & panelName() const
	{
		return mPanelName;
	}
	void setPanelName( const MString & name )
	{
		mPanelName.set( name.asChar() );
	}
	const MFloatPoint & viewRectangle() const
	{
		return mViewRectangle;
	}
	void setViewRectangle(const MFloatPoint & rect)
	{
		mViewRectangle = rect;
	}

protected:
	MString mPanelName;
	MHWRender::MCameraOverride mCameraOverride;
	MFloatPoint mViewRectangle;
	MHWRender::MRenderTarget* mTargets[2];
};

//------------------------------------------------------------------------
/*
	Custom render override class
*/
class viewRenderOverride : public MHWRender::MRenderOverride
{
public:
	enum {
		kBackgroundBlit = 0,
		// 3d scene render to target 1
		kMaya3dSceneRender,
		// Brightness threshold
		kThresholdOp,
		// Down sample to target 2
		kHorizBlurOp,
		kVertBlurOp,
		// Blend target 1 and 2 back to target 1
		kBlendOp,
		// Post ops on target 1
		kPostOperation1,
		kPostOperation2,
		// Post ui draw to target 1
		kMaya3dSceneRenderUI,
		// User op draw to target 1
		kUserOpNumber,
		// Present
		kPresentOp,
		// Draw HUD on top
		kHUDBlit,
		kNumberOfOps
	};

	viewRenderOverride( const MString & name )
		: MRenderOverride( name )
	{
		for (unsigned int i=0; i<kNumberOfOps; i++)
			mRenderOperations[i] = NULL;
		mCurrentOperation = -1;

		// Init target information for the override
		mTargetOverrideNames[0] = MString("__myCustomColorTarget__");
		mTargetOverrideNames[1] = MString("__myCustomDepthTarget__");
		MHWRender::MRasterFormat colorFormat = MHWRender::kR8G8B8A8_UNORM;
		MHWRender::MRasterFormat depthFormat = MHWRender::kD24S8;
		unsigned int sampleCount = 1; // no multi-sampling
		mTargetDescriptions[0] =
			new MHWRender::MRenderTargetDescription(mTargetOverrideNames[0], 256, 256, sampleCount, colorFormat, 0, false);
		mTargetDescriptions[1] =
			new MHWRender::MRenderTargetDescription(mTargetOverrideNames[1], 256, 256, sampleCount, depthFormat, 0, false);

		mTargets[0] = NULL;
		mTargets[1] = NULL;

		mBlurTargetName = MString("__myBlurTarget__");
		mBlurTargetDescription =
			new MHWRender::MRenderTargetDescription(mBlurTargetName, 256, 256, sampleCount, colorFormat, 0, false);
		mBlurTarget = NULL;

		// Set to true to split UI and non-UI draw
		mSplitUIDraw = false;

		// For debugging
		mDebugOverride = false;

		mPanelName.clear();
	}
	virtual ~viewRenderOverride()
	{
		// Cleanup targets
		if (mTargetDescriptions[0])
		{
			delete mTargetDescriptions[0];
			mTargetDescriptions[0] = NULL;
		}
		if (mTargetDescriptions[1])
		{
			delete mTargetDescriptions[1];
			mTargetDescriptions[1] = NULL;
		}
		if (mTargets[0])
		{
			delete mTargets[0]; mTargets[0] = NULL;
		}
		if (mTargets[1])
		{
			delete mTargets[1]; mTargets[1] = NULL;
		}

		if (mBlurTargetDescription)
		{
			delete mBlurTargetDescription;
			mBlurTargetDescription = NULL;
		}
		if (mBlurTarget)
		{
			delete mBlurTarget; mBlurTarget = NULL;
		}

		cleanup();

		// Delete all the operations
		for (unsigned int i=0; i<kNumberOfOps; i++)
		{
			if (mRenderOperations[i])
			{
				delete mRenderOperations[i];
				mRenderOperations[i] = NULL;
			}
		}
	}

	// Operation iterator methods
	virtual bool startOperationIterator()
	{
		mCurrentOperation = 0;
		return true;
	}
	virtual MHWRender::MRenderOperation * renderOperation()
	{
		if (mCurrentOperation >= 0 && mCurrentOperation < kNumberOfOps)
		{
			while(!mRenderOperations[mCurrentOperation])
			{
				mCurrentOperation++;
				if (mCurrentOperation >= kNumberOfOps)
				{
					return NULL;
				}
			}

			if (mRenderOperations[mCurrentOperation])
			{
				if (mDebugOverride)
				{
					printf("\t%s : Queue render operation[%d] = (%s)\n",
						mName.asChar(),
						mCurrentOperation,
						mRenderOperations[mCurrentOperation]->name().asChar());
				}
				return mRenderOperations[mCurrentOperation];
			}
		}
		return NULL;
	}
	virtual bool nextRenderOperation()
	{
		mCurrentOperation++;
		if (mCurrentOperation < kNumberOfOps)
		{
			return true;
		}
		return false;
	}

	virtual unsigned int renderOperationCount() const
	{
		return kNumberOfOps;
	}

	// Create the required render targets
	void updateRenderTargets()
	{
		if (mDebugOverride)
		{
			printf("\t%s : Set output render target overrides: color=%s, depth=%s\n",
				mName.asChar(),
				mTargetDescriptions[0]->name().asChar(), mTargetDescriptions[1]->name().asChar());
		}

		MHWRender::MRenderer *theRenderer = MHWRender::MRenderer::theRenderer();

        unsigned int targetWidth = 0;
        unsigned int targetHeight = 0;
        if( theRenderer )
		{
            theRenderer->outputTargetSize( targetWidth, targetHeight );
        }

		if (mTargetDescriptions[0]->width() != targetWidth ||
			mTargetDescriptions[1]->height() != targetHeight)
			{
				// A resize occured
			}

			// De-rez it just as an experiment.
			//viewWidth = viewWidth / 2;
			//viewHeight = viewHeight / 2;

		mTargetDescriptions[0]->setWidth( targetWidth );
		mTargetDescriptions[0]->setHeight( targetHeight );
		mTargetDescriptions[1]->setWidth( targetWidth );
		mTargetDescriptions[1]->setHeight( targetHeight );

		mBlurTargetDescription->setWidth( targetWidth );
		mBlurTargetDescription->setHeight( targetHeight );



		const MHWRender::MRenderTargetManager *targetManager = theRenderer ? theRenderer->getRenderTargetManager() : NULL;
		if (targetManager)
		{
			if (!mTargets[0])
			{
				mTargets[0] =  targetManager->acquireRenderTarget( *( mTargetDescriptions[0]) );
			}
			else
			{
				mTargets[0]->updateDescription( *( mTargetDescriptions[0]) );
			}
			if (!mTargets[1])
			{
				mTargets[1] =  targetManager->acquireRenderTarget( *( mTargetDescriptions[1]) );
			}
			else
			{
				mTargets[1]->updateDescription( *( mTargetDescriptions[1]) );
			}

			if (!mBlurTarget)
			{
				mBlurTarget =  targetManager->acquireRenderTarget( *mBlurTargetDescription );
			}
			else
			{
				mBlurTarget->updateDescription( *mBlurTargetDescription );
			}
		}

		// Update the render targets on the individual operations
		//
		viewRenderQuadRender *quadOp = (viewRenderQuadRender *)mRenderOperations[kBackgroundBlit];
		viewRenderSceneRender *sceneOp = (viewRenderSceneRender *)mRenderOperations[kMaya3dSceneRender];

		viewRenderQuadRender *thresholdOp = (viewRenderQuadRender *)mRenderOperations[kThresholdOp];
		viewRenderQuadRender *horizBlur = (viewRenderQuadRender *)mRenderOperations[kHorizBlurOp];
		viewRenderQuadRender *vertBlur = (viewRenderQuadRender *)mRenderOperations[kVertBlurOp];
		viewRenderQuadRender *blendOp = (viewRenderQuadRender *)mRenderOperations[kBlendOp];

		viewRenderSceneRender *sceneOp2 = (viewRenderSceneRender *)mRenderOperations[kMaya3dSceneRenderUI];
		viewRenderUserOperation *userOp = (viewRenderUserOperation *) mRenderOperations[kUserOpNumber];
		viewRenderQuadRender *quadOp2 = (viewRenderQuadRender *)mRenderOperations[kPostOperation1];
		viewRenderQuadRender *quadOp3 = (viewRenderQuadRender *)mRenderOperations[kPostOperation2];
		viewRenderPresentTarget *presentOp = (viewRenderPresentTarget *) mRenderOperations[kPresentOp];

		// Set the targets for blur
		if (mBlurTarget)
		{
			if (thresholdOp)
				thresholdOp->setRenderTarget(2, mBlurTarget);
			if (horizBlur)
				horizBlur->setRenderTarget(2, mBlurTarget);
			if (vertBlur)
				vertBlur->setRenderTarget(2, mBlurTarget);
			if (blendOp)
				blendOp->setRenderTarget(2, mBlurTarget);
		}

		if (mTargets[0])
		{
			if (quadOp)
				quadOp->setRenderTarget(0, mTargets[0]);
			if (sceneOp)
				sceneOp->setRenderTarget(0, mTargets[0]);
			if (sceneOp2)
				sceneOp2->setRenderTarget(0, mTargets[0]);
			if (quadOp2)
				quadOp2->setRenderTarget(0, mTargets[0]);
			if (quadOp3)
				quadOp3->setRenderTarget(0, mTargets[0]);
			if (userOp)
				userOp->setRenderTarget(0, mTargets[0]);
			presentOp->setRenderTarget(0, mTargets[0]);

			if (thresholdOp)
				thresholdOp->setRenderTarget(0, mTargets[0]);
			if (horizBlur)
				horizBlur->setRenderTarget(0, mTargets[0]);
			if (vertBlur)
				vertBlur->setRenderTarget(0, mTargets[0]);
			if (blendOp)
				blendOp->setRenderTarget(0, mTargets[0]);
		}
		if (mTargets[1])
		{
			if (quadOp)
				quadOp->setRenderTarget(1, mTargets[1]);
			if (sceneOp)
				sceneOp->setRenderTarget(1, mTargets[1]);
			if (sceneOp2)
				sceneOp2->setRenderTarget(1, mTargets[1]);
			if (quadOp2)
				quadOp2->setRenderTarget(1, mTargets[1]);
			if (quadOp3)
				quadOp3->setRenderTarget(1, mTargets[1]);
			if (userOp)
				userOp->setRenderTarget(1, mTargets[1]);
			presentOp->setRenderTarget(1, mTargets[1]);


			if (thresholdOp)
				thresholdOp->setRenderTarget(1, mTargets[1]);
			if (horizBlur)
				horizBlur->setRenderTarget(1, mTargets[1]);
			if (vertBlur)
				vertBlur->setRenderTarget(1, mTargets[1]);
			if (blendOp)
				blendOp->setRenderTarget(1, mTargets[1]);
		}

	}

	// Setup is just a regular render with no overrides
	virtual MStatus setup( const MString & destination )
	{
		if (mDebugOverride)
		{
			printf("%s : Perform setup with panel [%s]\n",
				mName.asChar(), destination.asChar() );
		}

		// Set the active panel if any
		mPanelName.set( destination.asChar() );

		if (mRenderOperations[kMaya3dSceneRender] == NULL)
		{
			// "Simple" means a scene draw + HUD + present to viewport
			if (mSimpleRendering)
			{
				mSplitUIDraw = false;

				mRenderOperations[kBackgroundBlit] = NULL;

				mRenderOperationNames[kMaya3dSceneRender] = "__MySimpleSceneRender";
				simpleViewRenderSceneRender *sceneOp = new simpleViewRenderSceneRender( mRenderOperationNames[kMaya3dSceneRender] );
				mRenderOperations[kMaya3dSceneRender] = sceneOp;

				mRenderOperations[kThresholdOp] = NULL;
				mRenderOperations[kHorizBlurOp] = NULL;
				mRenderOperations[kVertBlurOp] = NULL;
				mRenderOperations[kPostOperation1] = NULL;
				mRenderOperations[kPostOperation2] = NULL;
				mRenderOperations[kMaya3dSceneRenderUI] = NULL;
				mRenderOperations[kUserOpNumber] = NULL;

				mRenderOperations[kHUDBlit] = new MHWRender::MHUDRender();
				mRenderOperationNames[kHUDBlit] = (mRenderOperations[kHUDBlit])->name();

				mRenderOperationNames[kPresentOp] = "__MyPresentTarget";
				mRenderOperations[kPresentOp] = new viewRenderPresentTarget( mRenderOperationNames[kPresentOp] );
				mRenderOperationNames[kPresentOp] = (mRenderOperations[kPresentOp])->name();
			}
			else
			{
				MFloatPoint rect;
				rect[0] = 0.0f;
				rect[1] = 0.0f;
				rect[2] = 1.0f;
				rect[3] = 1.0f;

				// *. Pre scene quad render
				mRenderOperationNames[kBackgroundBlit] = "__MyPreQuadRender";
				viewRenderQuadRender *quadOp = new viewRenderQuadRender( mRenderOperationNames[kBackgroundBlit] );
				quadOp->setShader( viewRenderQuadRender::kPre_MandelBrot );
				quadOp->setViewRectangle(rect);
				mRenderOperations[kBackgroundBlit] = quadOp;

				// Turn this on to split UI and non-UI drawing passes
				//
				mSplitUIDraw = true;

				// *. 3d scene render
				if (mSplitUIDraw)
					mRenderOperationNames[kMaya3dSceneRender] = "__MyStdSceneRenderNonUI";
				else
					mRenderOperationNames[kMaya3dSceneRender] = "__MyStdSceneRender";
				viewRenderSceneRender *sceneOp = new viewRenderSceneRender( mRenderOperationNames[kMaya3dSceneRender] );
				sceneOp->setViewRectangle(rect);
				sceneOp->setSplitUI( mSplitUIDraw );
				mRenderOperations[kMaya3dSceneRender] = sceneOp;

				// *. 3d scene render UI
				if (mSplitUIDraw)
				{
					mRenderOperationNames[kMaya3dSceneRenderUI] = "__MyStdSceneRenderUI";
					viewRenderSceneRender *sceneOp2 = new viewRenderSceneRender( mRenderOperationNames[kMaya3dSceneRenderUI] );
					sceneOp2->setViewRectangle(rect);
					sceneOp2->setSplitUI( mSplitUIDraw );
					mRenderOperations[kMaya3dSceneRenderUI] = sceneOp2;
				}
				else
				{
					mRenderOperations[kMaya3dSceneRenderUI] = NULL;
				}

				mRenderOperationNames[kThresholdOp] = "__ThresholdColor";
				viewRenderQuadRender *quadThreshold  = new viewRenderQuadRender( mRenderOperationNames[kThresholdOp] );
				quadThreshold->setShader( viewRenderQuadRender::kScene_Threshold );
				quadThreshold->setViewRectangle(rect);
				mRenderOperations[kThresholdOp] = quadThreshold;

				mRenderOperationNames[kHorizBlurOp] = "__HorizontalBlur";
				viewRenderQuadRender *quadHBlur  = new viewRenderQuadRender( mRenderOperationNames[kHorizBlurOp] );
				quadHBlur->setShader( viewRenderQuadRender::kScene_BlurHoriz );
				quadHBlur->setViewRectangle(rect);
				mRenderOperations[kHorizBlurOp] = quadHBlur;

				mRenderOperationNames[kVertBlurOp] = "__VerticalBlur";
				viewRenderQuadRender *quadVBlur  = new viewRenderQuadRender( mRenderOperationNames[kVertBlurOp] );
				quadVBlur->setShader( viewRenderQuadRender::kScene_BlurVert );
				quadVBlur->setViewRectangle(rect);
				mRenderOperations[kVertBlurOp] = quadVBlur;

				mRenderOperationNames[kBlendOp] = "__SceneBlurBlend";
				viewRenderQuadRender *quadBlend  = new viewRenderQuadRender( mRenderOperationNames[kBlendOp] );
				quadBlend->setShader( viewRenderQuadRender::kSceneBlur_Blend );
				quadBlend->setViewRectangle(rect);
				mRenderOperations[kBlendOp] = quadBlend;

				// *. Post scene user callback
				mRenderOperationNames[kUserOpNumber] = "__MyCustomSceneRender";
				viewRenderUserOperation *userOp = new viewRenderUserOperation( mRenderOperationNames[kUserOpNumber] );
				userOp->setViewRectangle(rect);
				mRenderOperations[kUserOpNumber] = userOp;

				bool wantPostQuadOps = false;

				// *. Post scene quad render
				mRenderOperationNames[kPostOperation1] = "__PostOperation1";
				viewRenderQuadRender *quadOp2  = new viewRenderQuadRender( mRenderOperationNames[kPostOperation1] );
				quadOp2->setShader( viewRenderQuadRender::kPost_EffectMonochrome );
				quadOp2->setViewRectangle(rect);
				if (wantPostQuadOps)
					mRenderOperations[kPostOperation1] = quadOp2;
				else
					mRenderOperations[kPostOperation1] = NULL;

				mRenderOperationNames[kPostOperation2] = "__PostOperation2";
				viewRenderQuadRender *quadOp3  = new viewRenderQuadRender( mRenderOperationNames[kPostOperation2] );
				quadOp3->setShader( viewRenderQuadRender::kPost_EffectInvert );
				quadOp3->setViewRectangle(rect);
				if (wantPostQuadOps)
					mRenderOperations[kPostOperation2] = quadOp3;
				else
					mRenderOperations[kPostOperation2] = NULL;

				// *. Present target op
				mRenderOperationNames[kPresentOp] = "__MyPresentTarget";
				mRenderOperations[kPresentOp] = new viewRenderPresentTarget( mRenderOperationNames[kPresentOp] );
				mRenderOperationNames[kPresentOp] = (mRenderOperations[kPresentOp])->name();

				// *. Finally, a HUD render
				mRenderOperations[kHUDBlit] = new MHWRender::MHUDRender();
				mRenderOperationNames[kHUDBlit] = (mRenderOperations[kHUDBlit])->name();
			}
		}

		if (!mSimpleRendering)
		{
			updateRenderTargets();

			// Update the destination for ops
			((viewRenderSceneRender * )mRenderOperations[kMaya3dSceneRender])->setPanelName( mPanelName );
			if (mRenderOperations[kMaya3dSceneRenderUI])
				((viewRenderSceneRender * )mRenderOperations[kMaya3dSceneRenderUI])->setPanelName( mPanelName );
			if (mRenderOperations[kUserOpNumber])
				((viewRenderUserOperation *)mRenderOperations[kUserOpNumber])->setPanelName( mPanelName );
		}
		mCurrentOperation = -1;

		return MStatus::kSuccess;
	}
	virtual MStatus cleanup()
	{
		if (mDebugOverride)
		{
			printf("%s : Perform cleanup. panelname=%s\n", mName.asChar(), mPanelName.asChar());
		}

		viewRenderQuadRender *quadOp = (viewRenderQuadRender *) mRenderOperations[kPostOperation1];
		if (quadOp)
		{
			quadOp->setRenderTarget(0, NULL);
			quadOp->setRenderTarget(1, NULL);
		}
		quadOp = (viewRenderQuadRender *) mRenderOperations[kPostOperation2];
		if (quadOp)
		{
			quadOp->setRenderTarget(0, NULL);
			quadOp->setRenderTarget(1, NULL);
		}


		// Reset the active view
		mPanelName.clear();
		// Reset current operation
		mCurrentOperation = -1;

		return MStatus::kSuccess;
	}

	static MString gsVROName;

	const MString & panelName() const { return mPanelName; }

	void setSimpleRendering( bool flag )
	{
		mSimpleRendering = flag;
	}

protected:

	// Operation lists
	MHWRender::MRenderOperation * mRenderOperations[kNumberOfOps];
	MString mRenderOperationNames[kNumberOfOps];
	int mCurrentOperation;

	// Shared render target list
	MString mTargetOverrideNames[2];
	MHWRender::MRenderTargetDescription* mTargetDescriptions[2];
	MHWRender::MRenderTarget* mTargets[2];

	MString mBlurTargetName;
	MHWRender::MRenderTargetDescription* mBlurTargetDescription;
	MHWRender::MRenderTarget* mBlurTarget;

	// Override is for this panel
	MString mPanelName;

	// Split UI / nonUI draw
	bool mSplitUIDraw;

	// Simple rendering
	bool mSimpleRendering;

	bool mDebugOverride;
};



static viewRenderOverride *viewRenderOverrideInstance = NULL;
MString viewRenderOverride::gsVROName("my_viewRenderOverride");

//------------------------------------------------------------------------
///////////////////////////////////////////////////
//
// Command class declaration
//
// Example MEL usage:
//
// -- Set the override to be used for modelPane4
// viewRenderOverride modelPanel4;
//
// -- Remove the override from from being used for modelPanel4
// viewRenderOverride -remove modelPanel4;
//
///////////////////////////////////////////////////
class viewRenderOverrideCmd : public MPxCommand
{
public:
	viewRenderOverrideCmd();
	virtual                 ~viewRenderOverrideCmd();

	MStatus                 doIt( const MArgList& args );
	static MSyntax			newSyntax();
	static void*			creator();

private:
	MStatus                 parseArgs( const MArgList& args );

	// Name of panel
	MString					mPanelName;
	// Clear flag
	bool					mClearOverride;
	// "Simple" render flag
	bool					mSimpleRendering;
};

///////////////////////////////////////////////////
//
// Command class implementation
//
///////////////////////////////////////////////////

// Constructor
//
viewRenderOverrideCmd::viewRenderOverrideCmd()
{
	mPanelName = MString("");
	mClearOverride = false;
	mSimpleRendering = false;
}

// Destructor
//
viewRenderOverrideCmd::~viewRenderOverrideCmd()
{
	// Do nothing
}

// creator
//
void* viewRenderOverrideCmd::creator()
{
	return (void *) (new viewRenderOverrideCmd);
}

// Clear argument
const char *clearArgName = "-r";
const char *clearArgNameLongName = "-remove";

// "Simple" rendering argument
const char *simpleRenderArgName = "-s";
const char *simpleRenderArgNameLongName = "-simple";

// newSyntax
//
MSyntax viewRenderOverrideCmd::newSyntax()
{
	MSyntax syntax;

	syntax.addFlag(clearArgName, clearArgNameLongName);
	syntax.addFlag(simpleRenderArgName, simpleRenderArgNameLongName);

	// Name of model panel to override
	//
	syntax.addArg(MSyntax::kString);

	return syntax;
}

// parseArgs
//
MStatus viewRenderOverrideCmd::parseArgs(const MArgList& args)
{
	MStatus			status = MStatus::kFailure;
	MArgDatabase	argData(syntax(), args);

	MString     	arg;
	for ( unsigned int i = 0; i < args.length(); i++ )
	{
		arg = args.asString( i, &status );
		if (!status)
			continue;

		if ( arg == MString(clearArgName) || arg == MString(clearArgNameLongName) )
		{
			mClearOverride = true;
		}
		else if ( arg == MString(simpleRenderArgName) || arg == MString(simpleRenderArgNameLongName) )
		{
			mSimpleRendering = true;
		}
	}


	// Read off the panel name
	status = argData.getCommandArgument(0, mPanelName);
	if (!status)
	{
		status.perror("No panel name specified as command argument");
		return status;
	}
	return MStatus::kSuccess;
}

// doIt
//
MStatus viewRenderOverrideCmd::doIt(const MArgList& args)
{
	MStatus status = parseArgs(args);
	if (!status)
	{
		return status;
	}
	try
	{
        M3dView view;
        MStatus viewStatus = M3dView::getM3dViewFromModelPanel(mPanelName, view);

		MHWRender::MRenderer* renderer = MHWRender::MRenderer::theRenderer();
		if (mClearOverride)
		{
			if (viewRenderOverrideInstance)
			{
				MString emptyString;
				if( renderer )
				{
					// Remove the render override for batch render
					renderer->setRenderOverrideName( emptyString );
				}

				if( viewStatus == MS::kSuccess )
				{
					status = view.setRenderOverrideName( emptyString );
					if (status == MS::kSuccess)
					{
						printf("viewRenderOverride : Removed override for panel %s\n", mPanelName.asChar() );
					}
					else
					{
						printf("viewRenderOverride : Failed to remove override for panel %s\n", mPanelName.asChar() );
					}
				}
			}
		}
		else
		{
			// Register the override with this panel
			if (viewRenderOverrideInstance)
			{
				if( renderer )
				{
					// Set the render override name for batch render
					renderer->setRenderOverrideName( viewRenderOverride::gsVROName );
				}

				if( viewStatus == MS::kSuccess )
				{
					status = view.setRenderOverrideName( viewRenderOverride::gsVROName );
					if (status == MS::kSuccess)
					{
						// Set the panel name
						printf("viewRenderOverride : Set override %s for panel %s\n", viewRenderOverride::gsVROName.asChar(), mPanelName.asChar() );
					}
					else
					{
						printf("viewRenderOverride : Failed to set override %s for panel %s\n", viewRenderOverride::gsVROName.asChar(), mPanelName.asChar() );
					}

					viewRenderOverrideInstance->setSimpleRendering( mSimpleRendering );
				}
			}
		}
	}
	catch(MStatus ostatus)
	{
		return ostatus;
	}
	catch(...)
	{
		throw;
	}

	return status;
}

///////////////////////////////////////////////////
//
// Plug-in functions
//
///////////////////////////////////////////////////

MStatus initializePlugin( MObject obj )
{
	MStatus   status;
	MFnPlugin plugin( obj, PLUGIN_COMPANY, "1.0", "Any");


	// Register the command so we can actually do some work
	//
	status = plugin.registerCommand("viewRenderOverride",
									viewRenderOverrideCmd::creator,
									viewRenderOverrideCmd::newSyntax);

	if (!status)
	{
		status.perror("registerCommand");
	}
	else
	{
		// Create and register an override.
		if (!viewRenderOverrideInstance)
		{
			viewRenderOverrideInstance = new viewRenderOverride( "my_viewRenderOverride" );
			MHWRender::MRenderer* renderer = MHWRender::MRenderer::theRenderer();
			if (renderer && viewRenderOverrideInstance)
				renderer->registerOverride(viewRenderOverrideInstance);
		}
	}

	return status;
}

MStatus uninitializePlugin( MObject obj)
{
	MStatus   status;
	MFnPlugin plugin( obj );

	// Degister and delete override
	if (viewRenderOverrideInstance)
	{
		MHWRender::MRenderer* renderer = MHWRender::MRenderer::theRenderer();
		if (renderer && viewRenderOverrideInstance )
		{
			renderer->deregisterOverride(viewRenderOverrideInstance);
			delete viewRenderOverrideInstance;
		}
		viewRenderOverrideInstance = NULL;
	}

	// Deregister the command
	//
	status = plugin.deregisterCommand("viewRenderOverride");

	if (!status)
	{
		status.perror("deregisterCommand");
	}

	return status;
}

